﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using PropertyChanged;

namespace FFmpegVideoClip.Lib.ClipGroups
{
    [AddINotifyPropertyChangedInterface, Serializable]
    public class ClipsGroupManage
    {
        public static ClipsGroupManage? m_Instance;
        public static ClipsGroupManage Instance => m_Instance ??= new ClipsGroupManage();
        public static ReadOnlyCollection<ClipsGroup> AllGroup => new(Instance.Groups);
        public static int GroupCount => Instance.Groups.Count;
        public static readonly Dictionary<Clip, int> GroupReferenceKeyCount = new();
        public static int GroupsCount => Instance.Groups.Count;
        public static void GroupCreate(Clip MainClip) => Instance.CreateGroup(MainClip);
        public static void GroupDelet(Clip MainClip) => Instance.DeletGroup(MainClip);
        public static void Clear()
        {
            Instance.Groups.Clear();
            GroupReferenceKeyCount.Clear();
        }
        public static ClipsGroup? GroupGet(ClipGroupInfo? info) => Instance.GetGroup(info);
        public static bool TryGetGroup(ClipGroupInfo? info, out ClipsGroup Result) 
        {
            Result = null!;
            if (info == null)
                return false;
            Result = Instance.GetGroup(info)!;
            if (Result == null)
                return false;
            return true;
        }
        public static bool IsGroupReferenceKeyContain(Clip clip) => GroupReferenceKeyCount.ContainsKey(clip) && GroupReferenceKeyCount[clip] > 0;//(clip.Group == null ? 0 : 1);
        public static bool GetAssociatedGroup(Clip clip, out ClipsGroup[] Result)
        {
            Result = Instance.Groups.Where(p => p.IsContains(clip)).ToArray();
            if (Result.Length > 0)
                return true;
            return false;
        }
        public static bool GetAssociatedClip(Clip clip, out Clip[] Result)
        {
            Result = null!;
            if (!GetAssociatedGroup(clip, out ClipsGroup[] Groups))
                return false;
            Result = Groups.SelectMany(p => p.Clips).Distinct().ToArray();
            if (Result.Length > 0)
                return true;
            return false;
        }
        public static void FormAssociatedGroupRemove(IEnumerable<Clip> clips)
        {
            foreach(Clip clip in clips)
            {
                if(GetAssociatedGroup(clip, out ClipsGroup[] Groups))
                    Groups.ForEach(p=>p.RemoveChild(clip));
            }
        }
        public static ClipsGroupSaveData[]? FromClipManageCreateClipsGroupSaveDatas()
        {
            if (Instance.Groups == null || Instance.Groups.Count < 1)
                return null;
            try { return Instance.Groups.Select(ClipsGroupSaveData.FromClipManageCreate).ToArray(); }
            catch (Exception exc) { LogManage.WriteExceptionInfo($"生成组的保存数据异常", exc, true); }
            return null;
        }
        public static void LoadFromClipManage(ClipsGroupSaveData[]? datas)
        {
            Clear();
            if (datas == null || datas.Length < 1)
                return;
            try { Instance.Groups = new(datas.Select(ClipsGroupSaveData.FromClipManageCreate)); }
            catch (Exception exc) { LogManage.WriteExceptionInfo("读取数据组并构造实例数据异常", exc, true); }
            ClipManage.ClipList.ForEach(clip => clip.SendNotifyPropertyChanged(nameof(clip.IsExistsInGroup)));
        }
        
        /************************************************************************/

        public ObservableCollection<ClipsGroup> Groups { get;private set; } = new ObservableCollection<ClipsGroup>();
        public void CreateGroup(Clip MainClip)
        {
            try { InternalCreate(MainClip); }
            catch (Exception exc)
            {
                LogManage.WriteExceptionInfo("创建组操作错误:", exc, true);
                MainWindow.ShowStatusInfo(exc.Message);
            }
        }
        public void DeletGroup(Clip MainClip) 
        {
            try { InternalDeletGroup(MainClip); }
            catch (Exception exc)
            {
                LogManage.WriteExceptionInfo("删除组操作错误:", exc, true);
                MainWindow.ShowStatusInfo(exc.Message);
            }
        }
        public ClipsGroup? GetGroup(ClipGroupInfo? info) => Groups.FirstOrDefault(p => p.GroupID == (info?.GroupID ?? -200));
        public bool IsContains(Clip clip) => Groups.Any(p => p.GroupID == (clip.Group?.GroupID ?? -200));
        public bool GetClipsGroupFromChild(Clip clip, out ClipsGroup[] group)
        {
            group = Groups.Where(p => p.IsContains(clip)).ToArray();
            if (group.Length < 1)
                return false;
            return true;
        }

        private void InternalCreate(Clip MainClip)
        {
            if (MainClip is null)
                throw new ArgumentNullException($"参数[{nameof(MainClip)}] 不允许为Null");

            if (Groups.Any(p => p.MainClip == MainClip))
                throw new ArgumentException($"创建组异常: [{MainClip.Name}]中的组以存在");

            ClipsGroup group= new(MainClip, Groups.Any() ? Groups.Max(p => p.GroupID) + 1 : 1);
            Groups.Add(group);
            MainClip.Group = group;
        }
        private void InternalDeletGroup(Clip MainClip)
        {
            if (MainClip is null)
                throw new ArgumentNullException($"参数[{nameof(MainClip)}] 不允许为Null");
            ClipsGroup? group = GetGroup(MainClip.Group);
            if (group != null)
            {
                Groups.Remove(group);
                foreach (Clip clip in group.Clips)
                {
                    if (!GroupReferenceKeyCount.ContainsKey(clip))
                        continue;
                    int keyCount = GroupReferenceKeyCount[clip];
                    if (keyCount < 1)
                        GroupReferenceKeyCount.Remove(clip);
                    GroupReferenceKeyCount[clip] = keyCount - 1;
                    clip.SendNotifyPropertyChanged(nameof(clip.IsExistsInGroup));
                }
            }
            Groups.ForEach(p => 
            {
                if (p.Clips.Contains(MainClip))
                    p.Clips.Remove(MainClip);
            });
            MainClip.Group = null;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            Groups.ForEach(p => 
            {
                sb.AppendLine($"{nameof(p.MainClip)}:{p.MainClip.Name},{nameof(p.GroupID)}{p.GroupID}");
                sb.AppendLine("{");
                foreach (Clip clip in p.Clips) { sb.AppendLine($"  \"{clip.Name}\""); }
                sb.AppendLine("}");
            });
            return sb.ToString();
        }
    }

    [AddINotifyPropertyChangedInterface, Serializable]
    public class ClipsGroup
    {
        public Clip MainClip { get; set; }
        public int GroupID { get; private set; }
        public ObservableCollection<Clip> Clips { get; private set; } = new ObservableCollection<Clip>();
        public ClipsGroup(Clip mainClip, int ID) 
        {
            MainClip = mainClip; 
            GroupID = ID; 
            Clips.Add(mainClip);
            if (!ClipsGroupManage.GroupReferenceKeyCount.ContainsKey(mainClip))
                ClipsGroupManage.GroupReferenceKeyCount[mainClip] = 0;
            ClipsGroupManage.GroupReferenceKeyCount[mainClip] = ClipsGroupManage.GroupReferenceKeyCount[mainClip] + 1;
            Clips.CollectionChanged += Clips_CollectionChanged;
        }
        private void Clips_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                if (e.NewItems == null || e.NewItems.Count > 1)
                    return;
                foreach (Clip clip in e.NewItems)
                {
                    if (!ClipsGroupManage.GroupReferenceKeyCount.ContainsKey(clip))
                        ClipsGroupManage.GroupReferenceKeyCount.Add(clip, 0);
                    ClipsGroupManage.GroupReferenceKeyCount[clip] = ClipsGroupManage.GroupReferenceKeyCount[clip] + 1;
                    clip.SendNotifyPropertyChanged(nameof(clip.IsExistsInGroup));
                }
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
            {
                if (e.OldItems == null || e.OldItems.Count > 1)
                    return;
                foreach (Clip clip in e.OldItems)
                {
                    if (!ClipsGroupManage.GroupReferenceKeyCount.ContainsKey(clip))
                        continue;
                    int ReferenceKeyCount = ClipsGroupManage.GroupReferenceKeyCount[clip];
                    ClipsGroupManage.GroupReferenceKeyCount[clip] = --ReferenceKeyCount;
                    if (ReferenceKeyCount < 1)
                        ClipsGroupManage.GroupReferenceKeyCount.Remove(clip);
                    clip.SendNotifyPropertyChanged(nameof(clip.IsExistsInGroup));
                    if (clip == MainClip)
                        System.Windows.Application.Current.Dispatcher.BeginInvoke(() => Clips.Insert(0, clip));
                }
            }
        }
        public void Add(Clip clip)
        {
            if(!Clips.Contains(clip))
                Clips.Add(clip);
        }
        public void RemoveChild(Clip clip)
        {
            if(!Clips.Contains(clip) || clip == MainClip)
                return;
            Clips.Remove(clip);
        }
        public bool IsContains(Clip clip) => Clips.Contains(clip);
    }
    [AddINotifyPropertyChangedInterface, Serializable]
    public record class ClipGroupInfo(int GroupID, string GropuName)
    {
        public ClipGroupInfo(ClipsGroup Group) : this(Group.GroupID, Group.MainClip.Name) { }
        [JsonIgnore] public ObservableCollection<Clip> Clips => ClipsGroupManage.GroupGet(this)?.Clips ??
            throw new NullReferenceException($"未找到与[{nameof(ClipGroupInfo)}]对应的[{nameof(ClipsGroup)}]实例对象,[{nameof(ClipGroupInfo)}{{{nameof(GropuName)}:{GropuName},{nameof(GroupID)}:{GroupID}}}] ,组数据：[{ClipsGroupManage.Instance}]");
        public static implicit operator ClipGroupInfo(ClipsGroup Group) => new(Group);
    };

    public record class ClipsGroupSaveData(int GroupID, int MainClipID, int[] ClipsID)
    {
        public override string ToString() => System.Text.Json.JsonSerializer.Serialize(this, ClipManage.SerializerOptions);
        public static ClipsGroupSaveData FromClipManageCreate(ClipsGroup Group)
        {
            try 
            {
                ClipsGroupSaveData result = new(Group.GroupID, ClipManage.ClipList.IndexOf(Group.MainClip), Group.Clips.Select(p => ClipManage.ClipList.IndexOf(p)).ToArray());
                if (result.MainClipID < 0 || result.ClipsID.Any(p => p < 0))
                {
                    StringBuilder sb = new StringBuilder();
                    if (result.MainClipID < 0)
                        sb.Append($"MainClipName:[{Group.MainClip.Name}]");
                    foreach (Clip Item in Group.Clips)
                        sb.Append($"ClipName:[{Item.Name}]");
                    throw new IndexOutOfRangeException($"转换异常，组[{Group.MainClip.Name},ID:{Group.GroupID}]，于[{nameof(ClipManage.ClipList)}]中存在未匹配命中的项：{sb.ToString()}");
                }
                return result;
            }
            catch (Exception ex)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append($"GroupID:{Group.GroupID}  MainClipName:{Group.MainClip.Name}");
                foreach(var item in Group.Clips)
                    sb.Append($" ClipItemName{item.Name}");
                throw new InvalidCastException($"[{nameof(ClipsGroup)}]通过[{nameof(ClipManage)}]转换到[{nameof(ClipsGroupSaveData)}]异常 异常数据:{sb.ToString()}", ex);
            }

        }
        public static ClipsGroup FromClipManageCreate(ClipsGroupSaveData SaveData)
        {
            try 
            {
                ClipsGroup result = new(ClipManage.ClipList[SaveData.MainClipID], SaveData.GroupID);
                result.MainClip.Group = result;
                foreach (int index in SaveData.ClipsID)
                    result.Add(ClipManage.ClipList[index]);
                return result;
            }
            catch (Exception ex) 
            {
                throw new InvalidCastException($"[{nameof(ClipsGroupSaveData)}]通过[{nameof(ClipManage)}]转换到[{nameof(ClipsGroup)}]异常 异常数据:{SaveData},[{nameof(ClipManage.ClipList)}长度:[{ClipManage.ClipList.Count}]]", ex);
            }
        }
    
        
    }

}
